
#define DDCL_CORE


#include "ddclbase64.h"

DDCLAPI inline size_t
ddcl_base64_esize (size_t sz){
    return (sz + 2) / 3 * 4;
}

DDCLAPI inline size_t
ddcl_base64_dsize (size_t sz){
    return (sz + 3) / 4 * 3;
}

DDCLAPI void
ddcl_encode_base64 (
        const dduint8 * in, size_t sz, dduint8 * out){

    static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    size_t i, j;
    j = 0;
    dduint32 v;
    for (i = 0; i < sz - 2; i += 3){
        v = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2];
        out[j] = encoding[v >> 18];
        out[j + 1] = encoding[(v >> 12) & 0x3f];
        out[j + 2] = encoding[(v >> 6) & 0x3f];
        out[j + 3] = encoding[v & 0x3f];
        j += 4;
    }
    size_t padding = sz - i;
    switch(padding){
    case 1:
        v = in[i];
        out[j] = encoding[v >> 2];
        out[j + 1] = encoding[(v & 3) << 4];
        out[j + 2] = '=';
        out[j + 3] = '=';
        break;
    case 2:
        v = (in[i] << 8) | in[i + 1];
        out[j] = encoding[v >> 10];
        out[j + 1] = encoding[(v >> 4) & 0x3f];
        out[j + 2] = encoding[(v & 0xf) << 2];
        out[j + 3] = '=';
        break;
    }

}

static inline int
b64index (dduint8 c){
    static const int decoding[] =
        {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,
        -1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,
        12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,
        -1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,
        36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
    int sz = sizeof(decoding) / sizeof(decoding[0]);
    if(c < 43){ return -1; }
    c -= 43;
    if(c >= sz){ return -1; }
    return decoding[c];
}


DDCLAPI int
ddcl_decode_base64 (
        const dduint8 * in, size_t sz, dduint8 * out){

    size_t i, j, k, padding;
    k = padding = 0;
    for(i = 0; i < sz;){
        padding = 0;
        int c[4];
        for (j = 0; j < 4;){
            if(i >= sz){ return -1; }
            c[j] = b64index(in[i]);
            if (c[j] == -1) {
                ++i;
                continue;
            }
            if (c[j] == -2) { ++padding; }
            ++i;
            ++j;
        }
        dduint32 v;
        switch (padding) {
        case 0:
            v = ((dduint32)c[0] << 18) | (c[1] << 12) | (c[2] << 6) | c[3];
            out[k] = v >> 16;
            out[k+1] = (v >> 8) & 0xff;
            out[k+2] = v & 0xff;
            k += 3;
            break;
        case 1:
            if (c[3] != -2 || (c[2] & 3)!=0) {
                return -1;
            }
            v = ((dduint32)c[0] << 10) | (c[1] << 4) | (c[2] >> 2) ;
            out[k] = v >> 8;
            out[k+1] = v & 0xff;
            k += 2;
            break;
        case 2:
            if (c[3] != -2 || c[2] != -2 || (c[1] & 0xf) !=0)  {
                return -1;
            }
            v = (dduint32)c[0] << 2 | c[1] >> 4;
            out[k] = v;
            ++ k;
            break;
        default:
            return -1;
        }
    }
    return k;
}
